﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace FanaticToy.Core.Utils
{
    ///<summary>方法。</summary>
    public class MethodMetaUtils
    {
        ///<summary>方法有时候一定会用这些关键词来修饰。</summary>
        private static readonly string[] MethodKeywords = new string[]
        {
            "async", "public", "private","protected",
            "static", "virtual", "override", "abstract",
        };

        ///<summary>定义方法时绝对不会包括的一些关键词。</summary>
        private static readonly string[] MethodNotInclude = new string[]
        {
            "=>", ",", "class", "new", "var", "struct",
            "record", "=","return",":", "internal","{",
            "if","for","while","do","foreach","switch","case", "event"
        };

        public static bool TryGetMethodName(string codeLine, out string methodId)
        {
            methodId = string.Empty;
            if (string.IsNullOrWhiteSpace(codeLine)) return false;
            var leftCount = codeLine.Count(c => c == '(');
            if (codeLine.Contains(';')) return false;
            if (leftCount != 1) return false;
            var hasId = HasId(codeLine, out methodId);
            if (hasId == true) methodId = GetMethodIdIgnoreT(methodId);
            return hasId;
        }

        private static bool HasId(in string codeLine, out string methodId)
        {
            methodId = string.Empty;
            // 判断括号前 ( 是否有紧贴的 <>
            var firstLeftIndex = codeLine.IndexOf("(");
            var stack = new Stack<string>();
            var rightIndex = firstLeftIndex;

            // 括号的前一项必须有内容，不能是空格（虽然这个的定义并不是非常严格）。
            if (codeLine[rightIndex] == ' ') return false;

            var splitedCodeLineRaw = codeLine.Substring(0, firstLeftIndex);
            var splitedCodeLineRawParts = splitedCodeLineRaw.Split();

            if (splitedCodeLineRawParts.Length < 2) return false;
            if (splitedCodeLineRawParts.Last().StartsWith(".")) return false;

            foreach (string notInclude in MethodNotInclude)
            {
                if (splitedCodeLineRaw.Contains(notInclude)) return false;
            }

            if (splitedCodeLineRawParts.Length >= 3)
            {
                var other = splitedCodeLineRawParts[splitedCodeLineRawParts.Length - 3];
                foreach (string keyword in MethodKeywords)
                {
                    if (other == keyword)
                    {
                        methodId = splitedCodeLineRawParts.Last();
                        return true;
                    }
                }
                return false;
            }
            else
            {
                methodId = splitedCodeLineRawParts.Last();
                return true;
            }
        }

        ///<summary>获得当前方法。</summary>
        private static string GetMethodIdIgnoreT(string methodId)
        {
            if (methodId.Contains('<')) return methodId.Substring(0, methodId.IndexOf('<'));
            return methodId;
        }

        ///<summary>获得方法块。</summary>
        private static int GetMethodBlockEnd(List<string> lines, int startId)
        {
            var counter = 0;

            for (int i = startId; i < lines.Count; i++)
            {
                var cline = lines[i];
                foreach (char ch in cline)
                {
                    if (ch == '{')
                    {
                        counter++;
                    }
                    else if (ch == '}')
                    {
                        counter--;
                        if (counter == 0)
                            return i;
                    }
                }

            }

            if (counter != 0) return lines.Count - 1;

            return startId;
        }

        private static string SeekAndExtractMethodName(IEnumerable<string> lines, out int methodLineId)
        {
            methodLineId = -1;
            var startId = lines.Count();
            for (int i = startId - 1; i >= 0; i--)
            {
                var line = lines.ElementAtOrDefault(i);
                if (TryGetMethodName(line, out var methodName))
                {
                    methodLineId = i;
                    return methodName;
                }
            }
            return string.Empty;
        }

        ///<summary>获得当前 lineid 的方法的名称。</summary>
        public static string GetMethodNameByLineId(string filePath, int lineid)
        {
            var lineidFrom0 = lineid - 1;
            using (var fs = new FileStream(filePath, FileMode.Open, FileAccess.Read))
            {
                var list = new List<string>();
                var counter = 0;
                using (var sr = new StreamReader(fs))
                {
                    while (true)
                    {
                        var line = sr.ReadLine();
                        if (line == null) break;

                        list.Add(line);

                        // 当到达指定的位置之后，通过读取上下文来判断当前的光标位置是否在函数定义之内。
                        if (counter >= lineidFrom0)
                        {
                            // 在指定方法之后
                            var methodName = SeekAndExtractMethodName(list, out var methodLineId);
                            if (methodLineId < 0) return string.Empty;

                            var endId = GetMethodBlockEnd(list, methodLineId);

                            if (lineidFrom0 <= endId)
                            {
                                return methodName;
                            }
                            else
                            {
                                return string.Empty;
                            }
                        }

                        counter++;
                    }

                }
            }

            return string.Empty;
        }

    }
}
